#include <iostream>
#include <string.h>

using std::cout;
using std::endl;

//移动，就是拷贝和赋值函数的深拷贝变为浅拷贝，
//只改变指针指向，而不申请新空间+copystr，因为rhs是临时对象，用完就没了
//拷贝语义、移动语义
//具有移动语义的函数 优先于 具有拷贝语义的函数，所以传入右值时会先调用移动函数
class String{
public:
    String()
    : _pstr(nullptr) 
    {
        cout<<"String()"<<endl;
    }
    String(const char *pstr)
    : _pstr(new char[strlen(pstr)+1]())
    {
        strcpy(_pstr,pstr);
        cout<<"String(const char *pstr)"<<endl;
    }
    
    String(const String &rhs)
    : _pstr(new char[strlen(rhs._pstr)+1]())
    {
        cout<<"String(const String &rhs)"<<endl;
        strcpy(_pstr,rhs._pstr);
    }

    String &operator=(const String &rhs)
    {
        cout<<"String &operator=(const String &rhs)"<<endl;
        if(this!=&rhs){
            delete []_pstr;
            _pstr=nullptr;

            _pstr=new char[strlen(rhs._pstr)+1]();
            strcpy(_pstr,rhs._pstr);
        }
        return *this;
    }

    //移动构造函数
    String(String &&rhs)
    : _pstr(rhs._pstr)  //直接把s1的指针指向右值申请出来的空间
    {
        cout<<"String(String &&rhs)"<<endl;
        rhs._pstr=nullptr;
    }

    //移动赋值函数
    String &operator=(String &&rhs)
    {
        cout<<"String &operator=(String &&rhs)"<<endl;
        if(this!=&rhs){     
        //99%的情况下不用判断自赋自，但是有 自移自
        //这时候会让原本指的好好的_pstr=nullptr，丢失原有内容
            delete []_pstr;
            _pstr=nullptr;

            _pstr=rhs._pstr;
            rhs._pstr=nullptr;
        }
        return *this;
    }

    ~String(){
        cout<<"~String()"<<endl;
        if(_pstr){
            delete []_pstr;
            _pstr=nullptr;
        }
    }

    friend std::ostream &operator<<(std::ostream &os, const String &rhs);

private:
    char *_pstr;
};
std::ostream &operator<<(std::ostream &os, const String &rhs){
    if(rhs._pstr){
        os<<rhs._pstr;
    }
    else{
        os<<"nullptr"<<endl;
    }
    return os;
}

void test(){
    //c风格字符串---隐式转换---》c++风格字符串
    //先根据“hello”，调用构造函数String(const char *pstr)创建临时对象String("hello")
    //然后用临时对象给s1拷贝构造，然后马上释放临时对象
    //这样频繁申请释放堆空间，产生内存碎片
    //可以把 s1的指针 指向 临时对象申请出来的空间，这样少申请释放一次堆空间
    String s1="hello";  //隐式转换，String("hello")，临时对象

    cout<<endl;
    String s2("hello");
    s2=String("world"); //移动赋值函数
    cout<<s2<<endl;

    cout<<endl;
    String("beijing")=String("beijing");//两个临时对象，调用的是移动赋值函数
    
    cout<<endl<<"std::move()"<<endl;
    s2=std::move(s2);   //move返回右值
    cout<<&s2<<endl;    //但是s2仍是左值，说明move不改变s2
    //move本质上没有移动，即起不到移动赋值函数的作用
    //如果s2变为空指针，是因为移动赋值函数中没有判断自赋自，和move没有关系
    cout<<s2<<endl;

    cout<<endl;
    s2=std::move(s1);
    cout<<s1<<endl; //此时s1的_pstr已经指向空
    //右值引用也是引用。
    //在移动赋值函数中，传入的是变为右值的s1，而不是s1的临时右值对象
    //所以s1的_pstr被释放掉了
}

int main()
{   
    test();
    return 0;
}

